#
# Ronin Exploits - A Ruby library for Ronin that provides exploitation and
# payload crafting functionality.
#
# Copyright (c) 2007-2013 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin Exploits.
#
# Ronin is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ronin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ronin.  If not, see <http://www.gnu.org/licenses/>
#

require 'ronin/exploits/http'
require 'ronin/exploits/url_query_param_name'

require 'uri/query_params'

module Ronin
  module Exploits
    #
    # An {Exploit} class that represents exploits that run against Web
    # services.
    #
    class Web < HTTP

      # The targeted URL path
      property :url_path, String

      # The targeted URL query string
      property :url_query, String

      # The query-param that is vulnerable
      belongs_to :url_query_param, model:    'URLQueryParamName',
                                   required: false

      # Additional URL query params
      parameter :url_query_params, type:    Hash[String => String],
                                   default: {}

      # The HTTP method to use for requests
      parameter :http_method, type:        Symbol,
                              default:     :GET,
                              description: 'HTTP Method to use'

      # Additional HTTP Headers to send with the request
      parameter :http_headers, type:        Hash[String => String],
                               default:     {},
                               description: 'Additional HTTP Headers'

      #
      # Tests if the URI is vulnerable to the Web Exploit.
      #
      # @param [URI::HTTP, String] uri
      #   The URL to test.
      #
      # @param [Hash] options
      #   Additional options for {#initialize}.
      #
      # @return [Web]
      #   The first successful Web Exploit.
      #
      # @api public
      #
      def self.test(uri,options={})
        uri = URI(uri) unless uri.kind_of?(URI)

        uri.query_params.each do |name,value|
          exploit = new(options.merge(
            host:             uri.host,
            port:             uri.port,
            url_path:         uri.path,
            url_query:        uri.query,
            url_query_param:  URLQueryParamName.first_or_new(name: name)
          ))

          return exploit if exploit.vulnerable?
        end

        return nil
      end

      #
      # The value of the targeted query-param.
      #
      # @return [String]
      #   The value.
      #
      def url_query_param_value
        @url_query_param_value ||= (
          URI::QueryParams.parse(self.url_query)[self.url_query_param.name].to_s
        )
      end

      #
      # Builds the target URL based on the `#http_host`, `#http_port`,
      # {#url_prefix} and {#url_query_params} parameters as well as the
      # {#url_path} and {#url_query} properties.
      #
      # @return [URI::HTTP]
      #   The HTTP URI object.
      #
      # @see #url_for
      #
      def url
        path_query  = self.url_path
        path_query += "?#{self.url_query}" if self.url_query

        return url_for(path_query,self.url_query_params)
      end

      #
      # Contains the normal response for the URL.
      #
      # @return [Net::HTTPResponse]
      #   The normal HTTP response.
      #
      def normal_response
        @normal_response ||= http_request
      end

      #
      # Contains the normal response body for the URL.
      #
      # @return [String]
      #   The normal response body.
      #
      def normal_body
        normal_response.body
      end

      #
      # Creates an exploit URL.
      #
      # @param [#to_s] payload
      #   The payload to inject into the URL.
      #
      # @param [Hash] query_params
      #   Additional query parameters for the URL.
      #
      # @return [URL::HTTP]
      #   The URL which will trigger the exploit.
      #
      def exploit_url(payload,query_params={})
        new_url = url
        new_url.query_params.merge!(query_params)

        if self.url_query_param
          new_url.query_params[self.url_query_param.name] = payload
        end

        return new_url
      end

      #
      # Performs a HTTP request to the exploit URL.
      #
      # @param [#to_s] payload
      #   The payload to inject into the URL.
      #
      # @param [Hash] query_params
      #   Additional query parameters for the URL.
      #
      # @return [Net::HTTPResponse]
      #   The HTTP response from the exploit.
      #
      def exploit(payload,query_params={})
        http_request(url: exploit_url(payload,query_params))
      end

      #
      # Determines if the URL is vulnerable.
      #
      # @return [Boolean, nil]
      #   Specifies whether the URL is vulnerable to the exploit. Returns `nil`
      #   when it is unclear if the URL can be exploited.
      #
      # @abstract
      #
      def vulnerable?
        nil
      end

      protected

      #
      # Performs an HTTP request.
      #
      # @param [Hash] options
      #   Additional `http_request` options.
      #
      # @option options [Symbol] :method (self.http_method)
      #   The HTTP method to use for the request.
      #
      # @option options [Hash] :headers (self.http_headers)
      #   Additional HTTP Headers to send with the request.
      #
      # @yield [response]
      #   If a block is given, it will be passed the response received from
      #   the request.
      #
      # @yieldparam [Net::HTTPResponse] response
      #   The HTTP response object.
      #
      # @return [Net::HTTPResponse]
      #   The response of the HTTP request.
      #
      # @see http://ronin-ruby.github.com/docs/ronin-support/Ronin/Network/Mixins/HTTP.html#http_request-instance_method
      #
      def http_request(options={},&block)
        options = {
          method:  self.http_method,
          headers: self.http_headers,
          path:    self.url_path,
          query:   self.url_query
        }.merge(options)

        return super(options,&block)
      end

    end
  end
end
